<!DOCTYPE html>
<html lang="zh">

<head>
  <meta charset="UTF-8">
  <title>警报器覆盖模型demo</title>
</head>
<script src="https://lib.baomitu.com/echarts/5.1.2/echarts.min.js"></script>

<style>
  body {
    overflow: hidden;
  }


  #main {
    position: absolute;
    right: -150px;
    top: 20px;
  }

  .item_box {
    margin-bottom: 15px;
  }
</style>

<body>
  <table style="padding-top: 40px;">
    <tr>
      <div class="item_box">警报器个数<input type="text" id="size" value="30"></div>
      <div class="item_box">警报器最小覆盖半径<input type="text" id="minR" value="500"></div>
      <div class="item_box">警报器最大覆盖半径<input type="text" id="maxR" value="1000"></div>
      <div class="item_box">警报器半径处的声压级<input type="text" id="l" value="65"></div>
      <div class="item_box">左下角X<input type="text" id="x1" value="3000"></div>
      <div class="item_box">左下角Y<input type="text" id="y1" value="3000"></div>
      <div class="item_box">右上角X<input type="text" id="x2" value="16000"></div>
      <div class="item_box">右上角Y<input type="text" id="y2" value="8000"></div>

      <div class="item_box"><button onclick="craeteAlarm(calc,'click')">生成警报器</button></div>
    </tr>
  </table>
  <div class="item_box">计算方式:<input type="radio" name="inc" checked>
    快速计算<input type="radio" name="inc">精确计算</div>
  <!-- <button onclick="reCalc()">开始计算</button> -->

  <div id="main" style="width: 1800px; height: 900px;"></div>
  <script>
    var myChart;

    function calc(alarmList, x1, y1, x2, y2, m, n) {
      const ans = [];
      for (let i = 0; i < m; i++) {
        ans[i] = [];
        for (let j = 0; j < n; j++) {
          ans[i][j] = 0;
        }
      }
      const k = 4.0;
      const eps = 1e-3;
      const dx = (x2 - x1) / (n - 1);
      const dy = (y2 - y1) / (m - 1);
      for (let i = 0; i < alarmList.length; i++) {
        const alarm = alarmList[i]
        let x0 = alarm.x;
        let y0 = alarm.y;
        let r0 = alarm.r;
        let l0 = alarm.l;
        let n1 = Math.ceil((x0 - k * r0 - x1) / dx - eps);
        n1 = Math.max(n1, 0);
        let n2 = Math.floor((x0 + k * r0 - x1) / dx + eps);
        n2 = Math.min(n2, n - 1);
        let m1 = Math.ceil((y0 - k * r0 - y1) / dy - eps);
        m1 = Math.max(m1, 0);
        let m2 = Math.floor((y0 + k * r0 - y1) / dy + eps);
        m2 = Math.min(m2, m - 1);
        for (let i = m1; i <= m2; i++) {
          let y = y1 + dy * i;
          for (let j = n1; j <= n2; j++) {
            let x = x1 + dx * j;
            let r = ((x - x0) ** 2 + (y - y0) ** 2) ** 0.5;
            if (r > k * r0) {
              continue;
            }
            if (r < 1) {
              ans[i][j] = 150;
              continue;
            }
            let l = l0 + 20 * Math.log10(r0 / r);
            ans[i][j] = 10 * Math.log10(Math.pow(10, l / 10) + Math.pow(10, ans[i][j] / 10));
            ans[i][j] = Math.min(ans[i][j], 150);
          }
        }
      }
      return ans;
    }

    function calc2(alarmList, x1, y1, x2, y2, m, n) {
      const ans = [];
      for (let i = 0; i < m; i++) {
        ans[i] = [];
        for (let j = 0; j < n; j++) {
          ans[i][j] = 0;
        }
      }
      const dx = (x2 - x1) / (n - 1);
      const dy = (y2 - y1) / (m - 1);
      for (let i = 0; i < m; i++) {
        let y = y1 + dy * i;
        for (let j = 0; j < n; j++) {
          let x = x1 + dx * j;
          let sum = 0;
          for (let i = 0; i < alarmList.length; i++) {
            const alarm = alarmList[i]
            let r = Math.sqrt(Math.pow(x - alarm.x, 2) + Math.pow(y - alarm.y, 2));
            if (r < 1) {
              sum = 1e16;
              break;
            }
            let l = alarm.l + 20 * Math.log10(alarm.r / r);
            sum += Math.pow(10, l / 10);
          }
          ans[i][j] = 10 * Math.log10(sum);
          ans[i][j] = Math.min(ans[i][j], 150);
        }
      }
      return ans;
    }

    function generateAlarm(size, x1, y1, x2, y2, minR, maxR, l) {
      const alarmList = [];
      for (let i = 0; i < size; i++) {
        const alarm = {
          x: x1 + Math.random() * (x2 - x1),
          y: y1 + Math.random() * (y2 - y1),
          r: minR + Math.random() * (maxR - minR),
          l
        };
        alarmList.push(alarm);
      }
      return alarmList;
    }

    function calcData(alarmList, x1, y1, x2, y2, m, n, calc) {
      const data = [];
      const xData = [];
      const yData = [];
      const ans = calc(alarmList, x1, y1, x2, y2, m, n);
      for (let i = 0; i < m; i++) {
        for (let j = 0; j < n; j++) {
          data.push([j, i, ans[i][j]]);
        }
        yData.push(i);
      }
      for (let j = 0; j < n; j++) {
        xData.push(j);
      }
      return {
        data,
        xData,
        yData
      };
    }

    function init(size, x1, y1, x2, y2, minR, maxR, l, alarmList, m, n) {
      let calcc
      if (document.getElementsByName('inc')[1].checked) {
        calcc = calc2
      } else {
        calcc = calc
      }
      let {
        data,
        xData,
        yData
      } = calcData(alarmList, x1, y1, x2, y2, m, n, calcc)
      const chartDom = document.getElementById('main');
      myChart = echarts.init(chartDom);

      const option = {
        tooltip: {},
        xAxis: {
          type: 'category',
          data: xData
        },
        yAxis: {
          type: 'category',
          data: yData
        },
        visualMap: {
          min: 50,
          max: 110,
          range: [65, 110],
          calculable: true,
          realtime: false,
          inRange: {
            color: ['#313695', '#4575b4', '#74add1', '#abd9e9', '#e0f3f8', '#ffffbf', '#fee090', '#fdae61', '#f46d43',
              '#d73027', '#a50026'
            ]
          }
        },
        series: [{
          name: 'Gaussian',
          type: 'heatmap',
          data: data,
          emphasis: {
            itemStyle: {
              borderColor: '#333',
              borderWidth: 1
            }
          },
          progressive: 1000,
          animation: false
        }]
      };
      myChart.setOption(option);
    }



    let alarmList
    // 生成警报器
    function craeteAlarm(calc, flag, add) {
      let size = document.getElementById('size').value ? Number(document.getElementById('size').value) : 100;
      let x1 = document.getElementById('x1').value ? Number(document.getElementById('x1').value) : 0;
      let y1 = document.getElementById('y1').value ? Number(document.getElementById('y1').value) : 0;
      let x2 = document.getElementById('x2').value ? Number(document.getElementById('x2').value) : 20000;
      let y2 = document.getElementById('y2').value ? Number(document.getElementById('y2').value) : 10000;
      let minR = document.getElementById('minR').value ? Number(document.getElementById('minR').value) : 500;
      let maxR = document.getElementById('maxR').value ? Number(document.getElementById('maxR').value) : 1000;
      let l = document.getElementById('l').value ? Number(document.getElementById('l').value) : 65;
      let m = 75,
        n = 150
      if (!add) {
        alarmList = generateAlarm(size, x1, y1, x2, y2, minR, maxR, l);
      }
      if (flag) {
        reCalc(size, x1, y1, x2, y2, minR, maxR, l, m, n)
      } else {
        init(size, 0, 0, 20000, 10000, minR, maxR, l, alarmList, m, n)
      }
    }
    craeteAlarm(calc)


    // 快速计算和精确计算
    function reCalc(size, x1, y1, x2, y2, minR, maxR, l, m, n) {
      console.log(document.getElementsByName('inc')[1].checked);
      let calcc
      if (document.getElementsByName('inc')[1].checked) {
        calcc = calc2
      } else {
        calcc = calc
      }
      alarmList = alarmList ? alarmList : generateAlarm(size, x1, y1, x2, y2, minR, maxR, l);
      console.log(alarmList);
      let {
        _,
        data,
        xData,
        yData
      } = calcData(alarmList, 0, 0, 20000, 10000, m, n, calcc);
      const option = myChart.getOption();
      option.series[0].data = data;
      myChart.setOption(option);
    }
    let array = document.getElementsByName('inc')
    array[0].onclick = function (e) {
      craeteAlarm(calc, 'click', 'add')
    }
    array[1].onclick = function (e) {
      craeteAlarm(calc, 'click', 'add')
    }

    myChart.on('click', function (e) {
      console.log(e.data);
      let promptext = prompt('请输入警报器覆盖半径', 1000);
      console.log(promptext);
      if (!promptext) {
        return
      }
      let x1 = document.getElementById('x1').value ? Number(document.getElementById('x1').value) : 0;
      let y1 = document.getElementById('y1').value ? Number(document.getElementById('y1').value) : 0;
      let x2 = document.getElementById('x2').value ? Number(document.getElementById('x2').value) : 20000;
      let y2 = document.getElementById('y2').value ? Number(document.getElementById('y2').value) : 10000;
      let m = 75,
        n = 150
      let obj = {
        x: e.data[0] * (20000 - 0) / (n - 1),
        y: e.data[1] * (10000 - 0) / (m - 1),
        r: promptext,
        l: document.getElementById('l').value ? Number(document.getElementById('l').value) : 65
      }
      alarmList.push(obj)
      craeteAlarm(calc, 'click', 'add')
    })
  </script>
</body>

</html>